package com.elai.analytics.controller;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.elai.analytics.domain.AnalyzeFunnelDO;
import com.elai.analytics.service.AnalyzeFunnelService;
import com.elai.baseconfig.domain.EventTypeDO;
import com.elai.baseconfig.service.EventTypeService;
import com.elai.common.annotation.Log;
import com.elai.common.constant.BusinessConstants;
import com.elai.common.constant.HttpStatus;
import com.elai.common.core.controller.BaseController;
import com.elai.common.core.domain.AjaxResult;
import com.elai.common.core.domain.entity.SysUser;
import com.elai.common.core.page.TableDataInfo;
import com.elai.common.enums.BusinessType;
import com.elai.common.utils.*;
import com.elai.analytics.domain.AnalyzeFunnelQueryDo;
import com.elai.analytics.domain.ColumnHeader;
import com.elai.analytics.domain.QueryParam;
import com.elai.analytics.domain.third.CkFunneResVO;
import com.elai.common.utils.poi.ExcelUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 分析漏斗Controller
 *
 * @author elai
 * @date 2021-05-01
 */
@RestController
@RequestMapping("/config/funnel")
public class AnalyzeFunnelController extends BaseController
{
    private static final Logger log = LoggerFactory.getLogger(KeepAnalyticsController.class);

    @Autowired
    private AnalyzeFunnelService analyzeFunnelService;

    @Autowired
    private EventTypeService eventTypeService;

    @Autowired
    private ThirdProperties thirdProperties;

    //漏斗分析
    private static final String FUNNEL_ANALYSIS ="funnel/findFunnel";

    /**
     * 查询分析漏斗列表
     */
    @PreAuthorize("@ss.hasPermi('config:funnel:list')")
    @GetMapping("/list")
    public TableDataInfo list(AnalyzeFunnelDO analyzeFunnel)
    {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(!currentUser.isAdmin()){
            analyzeFunnel.setLesseeId(currentUser.getLesseeId());
        }
        startPage();
        List<AnalyzeFunnelDO> list = analyzeFunnelService.selectAnalyzeFunnelList(analyzeFunnel);
        return getDataTable(list);
    }


    @GetMapping("allFunnelList")
    public AjaxResult allFunnelList(AnalyzeFunnelDO analyzeFunnel)
    {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(!currentUser.isAdmin()){
            analyzeFunnel.setLesseeId(currentUser.getLesseeId());
        }
        List<AnalyzeFunnelDO> list = analyzeFunnelService.selectAnalyzeFunnelList(analyzeFunnel);
        return AjaxResult.success(list);
    }


    /**
     * 导出分析漏斗列表
     */
    @PreAuthorize("@ss.hasPermi('config:funnel:export')")
    @Log(title = "分析漏斗", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(AnalyzeFunnelDO analyzeFunnel)
    {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(!currentUser.isAdmin()){
            analyzeFunnel.setLesseeId(currentUser.getLesseeId());
        }
        List<AnalyzeFunnelDO> list = analyzeFunnelService.selectAnalyzeFunnelList(analyzeFunnel);
        ExcelUtil<AnalyzeFunnelDO> util = new ExcelUtil<AnalyzeFunnelDO>(AnalyzeFunnelDO.class);
        return util.exportExcel(list, "funnel");
    }

    /**
     * 获取分析漏斗详细信息
     */
    @PreAuthorize("@ss.hasPermi('config:funnel:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id)
    {
        return AjaxResult.success(analyzeFunnelService.selectAnalyzeFunnelById(id));
    }

    /**
     * 新增分析漏斗
     */
    @Log(title = "分析漏斗", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody AnalyzeFunnelDO analyzeFunnel)
    {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        analyzeFunnel.setLesseeId(currentUser.getLesseeId());
        int result=analyzeFunnelService.insertAnalyzeFunnel(analyzeFunnel);
        return AjaxResult.success(analyzeFunnel.getId());
    }

    /**
     * 修改分析漏斗
     */
    @Log(title = "分析漏斗", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody AnalyzeFunnelDO analyzeFunnel)
    {
        return toAjax(analyzeFunnelService.updateAnalyzeFunnel(analyzeFunnel));
    }

    /**
     * 删除分析漏斗
     */
    @Log(title = "分析漏斗", businessType = BusinessType.DELETE)
	@DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids)
    {
        return toAjax(analyzeFunnelService.deleteAnalyzeFunnelByIds(ids));
    }

    @PostMapping("/eventAnalyzeFunnel")
    public AjaxResult eventAnalyzeCharData(@RequestBody String requestJson)
    {
        log.info("eventAnalyzeFunnel："+requestJson);
        JSONObject paramJson = JSONObject.parseObject(requestJson);
        JSONObject funnelJson = paramJson.getJSONObject("requestJson");

        String startDate = null;
        String endDate = null;

        JSONArray dateArray = funnelJson.getJSONArray("dateRange");
        if (JSONUtil.isNull(dateArray)){
            startDate = DateUtils.getYYYYMMDD(-6);
            endDate = DateUtils.getYYYYMMDD(-1);
        }else{
            String tempStartDate = dateArray.getString(0);
            String tempEndDate = dateArray.getString(1);
            startDate = tempStartDate.length()>10?tempStartDate.substring(0,10):tempStartDate;
            endDate = tempEndDate.length()>10?tempEndDate.substring(0,10):tempEndDate;
        }
        //概要基础指标来源
        if (startDate.equals(endDate) && funnelJson.containsKey("fromQuery")){
            startDate = DateUtils.getOneDayYYYYMMDD(endDate,-6);
        }

//        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
//        EventTypeDO typeParam = new EventTypeDO();
//        typeParam.setLesseeId(currentUser.getLesseeId());
        List<EventTypeDO> eventTypeList = eventTypeService.selectAllEventType();

        Map<String, Object> paramMap =new HashMap();
        paramMap.put("startDate",startDate);
        paramMap.put("endDate",endDate);
        int windowFunnel = 7;
        String windowFunnelUnit = "day";
        AnalyzeFunnelDO analyzeFunnelDO = getAnalyzeFunnelDO(funnelJson);
        if (analyzeFunnelDO != null){
            windowFunnel = analyzeFunnelDO.getWindowDate().intValue();
            windowFunnelUnit = analyzeFunnelDO.getWindowUnit();
        }
        paramMap.put("windowFunnel",windowFunnel); //窗口数
        paramMap.put("windowFunnelUnit",windowFunnelUnit); //窗口单位

        //获取漏斗步骤
        List<String> events = getFunnelEvents(funnelJson);
        List<Map<String,String>> funnelEventList = getFunnelEventList(funnelJson);

        paramMap.put("funnelEvents",events);
        paramMap.put("funnelEventList",funnelEventList);
        paramMap.put("jsonParam",funnelJson);
        if (funnelJson.containsKey("applicationCode")){
            JSONArray applicationCodeList = funnelJson.getJSONArray("applicationCode");
            if (applicationCodeList != null && !applicationCodeList.isEmpty()){
                paramMap.put("applicationCodeList",applicationCodeList.toJavaList(String.class));
            }
        }

        JSONObject obj= WebUtils.postCkObj(thirdProperties.getCkUrl(),FUNNEL_ANALYSIS,paramMap);
        if(obj==null){
            return AjaxResult.success(new HashMap<>());
        }
        int subcode = obj.getInteger("code");
        List<CkFunneResVO> funneResList=new ArrayList<>();
        if(HttpStatus.CK_SUCCESS == subcode){
            funneResList = JSON.parseArray(obj.getString("data"), CkFunneResVO.class);
        }
        Map<String, List<CkFunneResVO>> groupList = funneResList.stream().collect(
                Collectors.groupingBy(a -> a.getResDate()));
//        Map<String, List<CkFunneResVO>> mapList = new HashMap<>();
        //Comparator.reverseOrder()
        Map<String, List<CkFunneResVO>> mapList = groupList.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

        Integer allCount = funneResList.stream().mapToInt(CkFunneResVO::getLevelCount).sum();

        Map<String,Object> resultMap=new HashMap();

        List<ColumnHeader> tableTitle=new ArrayList<ColumnHeader>();
        tableTitle.add(new ColumnHeader("total","","",false));

        List tableData=new ArrayList();

        List<Map<String,Object>> transResult=new ArrayList();
        List<Map<String,Integer>> eventCountList = new ArrayList<>();
        Map<String,Integer> eventCountMap = new HashMap<>();
        Map<String,String> infoData = new HashMap<>();
        int eventSize = events.size();
        int allEventCount = 0;
        int lastEventCount = 0;
        int i=0;
        for (String event : events){
            //过滤出符合条件的数据
            List<CkFunneResVO> filterList = funneResList.stream().filter(a -> event.equals(a.getEventCode())).collect(Collectors.toList());
            int eventCount = filterList.stream().mapToInt(CkFunneResVO::getLevelCount).sum();
            //第1个步骤事件的总数量
            if (i ==0){
                allEventCount = eventCount;
//                infoData.put("transPercent",percent+"%");
            }
            //最后步骤事件的总数量
            if (i == eventSize-1){
                lastEventCount = eventCount;
            }
            BigDecimal percent = allCount==0?BigDecimal.ZERO: NumbericUtils.calcDivide(eventCount,allCount,2);
            Map<String,Object> step=new HashMap();
            step.put("stepName",getEventName(event,eventTypeList));
            step.put("transPercent",percent);
            step.put("personNum",eventCount);
            transResult.add(step);

            i++;
            tableTitle.add(new ColumnHeader(event,getEventName(event,eventTypeList),"",true));

            eventCountMap.put(event,eventCount);

        }

        List childDataList=new ArrayList();
        List title=new ArrayList();
        List<Map<String,Object>> chartList = new ArrayList<>();
        List<Integer> charDataList=new ArrayList<>();
        for (Map.Entry<String, List<CkFunneResVO>> m : mapList.entrySet()) {
            Map childData=new HashMap();
            childData.put("id",m.getKey());
            childData.put("eventName",getEventName(m.getKey(),eventTypeList));
            int total = 0;
            for (int n=0;n<m.getValue().size();n++){
                CkFunneResVO vo  = m.getValue().get(n);
                childData.put(vo.getEventCode(),vo.getLevelCount());
                if (n+1 == m.getValue().size()){
                    total = vo.getLevelCount();
                }
            }
//            childData.put("total",total);
            childData.put("total",m.getKey());
            childDataList.add(childData);


            title.add(m.getKey());

            charDataList.add(total);

        }

        Map rowData=new HashMap();
        rowData.put("id",100);
        int eventTotal = 0;
        for (String event : events){
            rowData.put(event,eventCountMap.get(event));
            eventTotal = eventTotal + eventCountMap.get(event);
        }
        rowData.put("total","总体");
        rowData.put("children",childDataList);
        tableData.add(rowData);

        Map chartData=new HashMap();
        chartData.put("type","line");

        chartData.put("data",charDataList);

        resultMap.put("funnelData",transResult);
        resultMap.put("tableTitle",tableTitle);
        resultMap.put("tableData",tableData);

        resultMap.put("chartData",chartData);
        resultMap.put("chartTitle",title);

        infoData.put("gmvStartDate",startDate);
        infoData.put("gmvEndDate",endDate);
        //总转化率
        infoData.put("allTransPercent",(allCount==0?BigDecimal.ZERO: NumbericUtils.calcDivide(lastEventCount,allEventCount,2))+"%");

        resultMap.put("infoData",infoData);
        return AjaxResult.success(resultMap);
    }

    private String getEventName(String code,List<EventTypeDO> eventTypeList){
        Optional<EventTypeDO> optional = eventTypeList.stream().filter(property -> property.getEventType().equals(code)).findFirst();
        if (optional.isPresent()) {
            // 存在
            EventTypeDO org =  optional.get();
            return org.getEventTypeName();
        } else {
            // 不存在
        }
        return code;
    }

    private List<String> getFunnelEvents(JSONObject funnelJson){
        List<String> events = new ArrayList<>();
        if (funnelJson.getJSONObject("funnelInfo") == null){
            return getFunnelEventsById(funnelJson);
        }
        if (!funnelJson.getJSONObject("funnelInfo").containsKey("funnelSteps")){
            return getFunnelEventsById(funnelJson);
        }
        JSONArray funnelSteps = funnelJson.getJSONObject("funnelInfo").getJSONArray("funnelSteps");
        if (funnelSteps == null){
            return getFunnelEventsById(funnelJson);
        }

        for (int i=0;i< funnelSteps.size();i++){
            JSONObject jsonObject = funnelSteps.getJSONObject(i);
            JSONObject eventTypeJson = jsonObject.getJSONObject("eventType");
            events.add(eventTypeJson.getString("eventType"));
        }
        return events;

    }

    private List<String> getFunnelEventsById(JSONObject funnelJson){
        List<String> events = new ArrayList<>();
        if (funnelJson.getLong("funnelId") != null && funnelJson.getLong("funnelId") > 0){
            AnalyzeFunnelDO funnelDO = analyzeFunnelService.selectAnalyzeFunnelById(funnelJson.getLong("funnelId"));
            if (StringUtils.isNotEmpty(funnelDO.getFunnelSteps())){
                JSONArray funnelSteps = JSON.parseArray(funnelDO.getFunnelSteps());
                for (int i=0;i< funnelSteps.size();i++){
                    JSONObject jsonObject = funnelSteps.getJSONObject(i);
                    JSONObject eventTypeJson = jsonObject.getJSONObject("eventType");
                    events.add(eventTypeJson.getString("eventType"));
                }
            }
        }else{
            events.add("MPViewScreen");
            events.add("MPClick");
        }
        return events;
    }

    private AnalyzeFunnelDO getAnalyzeFunnelDO(JSONObject funnelJson){
        if (funnelJson.getJSONObject("funnelInfo") == null){
            return getAnalyzeFunnelDOById(funnelJson);
        }
        String infoStr = funnelJson.getString("funnelInfo");
        AnalyzeFunnelDO funnelDO = JSONObject.parseObject(infoStr,AnalyzeFunnelDO.class);
        return funnelDO;
    }

    private AnalyzeFunnelDO getAnalyzeFunnelDOById(JSONObject funnelJson){
        if (funnelJson.getLong("funnelId") != null && funnelJson.getLong("funnelId") > 0){
            AnalyzeFunnelDO funnelDO = analyzeFunnelService.selectAnalyzeFunnelById(funnelJson.getLong("funnelId"));
            return funnelDO;
        }
        return null;
    }

    private List<Map<String,String>> getFunnelEventList(JSONObject funnelJson){
        List<Map<String,String>> events = new ArrayList<>();
        if (funnelJson.getJSONObject("funnelInfo") == null){
            return getFunnelEventListById(funnelJson);
        }
        if (!funnelJson.getJSONObject("funnelInfo").containsKey("funnelSteps")){
            return getFunnelEventListById(funnelJson);
        }
        JSONArray funnelSteps = funnelJson.getJSONObject("funnelInfo").getJSONArray("funnelSteps");
        if (funnelSteps == null){
            return getFunnelEventListById(funnelJson);
        }

        for (int i=0;i< funnelSteps.size();i++){
            JSONObject jsonObject = funnelSteps.getJSONObject(i);
            JSONObject eventTypeJson = jsonObject.getJSONObject("eventType");
            Map<String,String> map = new HashMap<>();
            map.put("eventCode",eventTypeJson.getString("eventType"));
            map.put("eventName",eventTypeJson.getString("eventTypeName"));
            map.put("eventCategory",eventTypeJson.getString("categoryType"));
            if (BusinessConstants.ct_virtual.equals(eventTypeJson.getString("categoryType"))){
                map.put("eventValue",getVirtualEventValue(eventTypeJson));
            }else{
                map.put("eventValue",eventTypeJson.getString("eventType"));
            }

            events.add(map);
        }
        return events;

    }

    private List<Map<String,String>> getFunnelEventListById(JSONObject funnelJson){
        List<Map<String,String>> events = new ArrayList<>();
        if (funnelJson.getLong("funnelId") != null && funnelJson.getLong("funnelId") > 0){
            AnalyzeFunnelDO funnelDO = analyzeFunnelService.selectAnalyzeFunnelById(funnelJson.getLong("funnelId"));
            if (StringUtils.isNotEmpty(funnelDO.getFunnelSteps())){
                JSONArray funnelSteps = JSON.parseArray(funnelDO.getFunnelSteps());
                for (int i=0;i< funnelSteps.size();i++){
                    JSONObject jsonObject = funnelSteps.getJSONObject(i);
                    JSONObject eventTypeJson = jsonObject.getJSONObject("eventType");
                    Map<String,String> map = new HashMap<>();
                    map.put("eventCode",eventTypeJson.getString("eventType"));
                    map.put("eventName",eventTypeJson.getString("eventTypeName"));
                    map.put("eventCategory",eventTypeJson.getString("categoryType"));
                    if (BusinessConstants.ct_virtual.equals(eventTypeJson.getString("categoryType"))){
                        map.put("eventValue",getVirtualEventValue(eventTypeJson));
                    }else{
                        map.put("eventValue",eventTypeJson.getString("eventType"));
                    }
                    events.add(map);
                }
            }
        }else{
            Map<String,String> map1 = new HashMap<>();
            map1.put("eventCode","MPViewScreen");
            map1.put("eventName","MPViewScreen");
            map1.put("eventValue","MPViewScreen");
            map1.put("eventCategory","normal");
            events.add(map1);
            Map<String,String> map2 = new HashMap<>();
            map2.put("eventCode","MPClick");
            map2.put("eventName","MPClick");
            map2.put("eventValue","MPClick");
            map1.put("eventCategory","normal");
            events.add(map1);
        }
        return events;
    }

    private String getVirtualEventValue(JSONObject virtualParam){
        StringBuffer values = new StringBuffer();
        if (virtualParam.containsKey("eventJson")){
            JSONArray jsonArray = virtualParam.getJSONArray("eventJson");
            for(int i=0;i<jsonArray.size();i++){
                JSONObject jsonParam = jsonArray.getJSONObject(i);
                if (i != 0){
                    values.append(",");
                }
                values.append("'");
                values.append(jsonParam.getString("eventType"));
                values.append("'");
            }

        }

        return values.toString();
    }
}
